package com.hyt.it.ogt.ks.service.impl;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hyt.core.base.BaseServiceImpl;
import com.hyt.core.util.UUIDUtils;
import com.hyt.it.ogt.ks.api.kw.model.ApplyCheckDTO;
import com.hyt.it.ogt.ks.api.kw.model.PageApplyCheckDTO;
import com.hyt.it.ogt.ks.enums.*;
import com.hyt.it.ogt.ks.enums.log.ApplyCheckState;
import com.hyt.it.ogt.ks.enums.log.CandidateOperatorLogType;
import com.hyt.it.ogt.ks.enums.log.LogEventType;
import com.hyt.it.ogt.ks.enums.log.WebsocketMsgSendState;
import com.hyt.it.ogt.ks.feign.KwClient;
import com.hyt.it.ogt.ks.feign.es.EsClient;
import com.hyt.it.ogt.ks.feign.model.ExamDTO;
import com.hyt.it.ogt.ks.feign.model.KwExamCandidate;
import com.hyt.it.ogt.ks.mapper.ApplyCheckMapper;
import com.hyt.it.ogt.ks.model.entity.ApplyCheck;
import com.hyt.it.ogt.ks.model.entity.ApplyCheckMsg;
import com.hyt.it.ogt.ks.model.vo.*;
import com.hyt.it.ogt.ks.service.*;
import com.hyt.it.ogt.ks.websocket.MessageContentDTO;
import com.hyt.it.ogt.ks.websocket.WebsocketSendMessageDTO;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 跳过申请审核表 服务实现类
 * </p>
 *
 * @author liteng
 * @since 2022-11-30
 */
@Service
@Slf4j
public class ApplyCheckServiceImpl extends BaseServiceImpl<ApplyCheckMapper, ApplyCheck> implements IApplyCheckService {
    @Resource
    private EsClient esClient;
    @Resource
    private ILogService iLogService;
    @Resource
    private ICandidatePassService candidatePassService;
    @Resource
    private KwClient kwClient;
    @Resource
    private IApplyCheckMsgService applyCheckMsgService;
    @Resource
    private ExamInfoCacheService examInfoCacheService;

    @Override
    public Page<ApplyCheck> pageApplyCheck(@NonNull PageApplyCheckDTO pageApplyCheckDTO) {
        String examId = pageApplyCheckDTO.getExamId();
        Integer state = pageApplyCheckDTO.getState();
        LambdaQueryWrapper<ApplyCheck> applyCheckLambdaQueryWrapper = new LambdaQueryWrapper<>();
        applyCheckLambdaQueryWrapper.eq(ApplyCheck::getExamId, examId)
                .eq(ApplyCheck::getState, state);
        return page(pageApplyCheckDTO, applyCheckLambdaQueryWrapper);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApplyCheck applyCheck(@NonNull ApplyCheckDTO applyCheck) {
        ApplyCheck dbApplyCheck = getById(applyCheck.getId());
        if (Objects.isNull(dbApplyCheck)) {
            throw new RuntimeException(String.format("待审核不存在 %s", applyCheck.getId()));
        }

        if (!Objects.equals(ApplyCheckState.WAIT_HANDLE.getCode(), dbApplyCheck.getState())) {
            throw new RuntimeException(String.format("已经审核过 %s", applyCheck.getId()));
        }

        // 审核结果入库
        LocalDateTime now = LocalDateTime.now();
        boolean applySuccess = lambdaUpdate()
                .eq(ApplyCheck::getId, applyCheck.getId())
                .set(ApplyCheck::getState, applyCheck.getState())
                .set(ApplyCheck::getUpdateDate, now)
                .set(ApplyCheck::getOperateUser, applyCheck.getOperateUser())
                .update();
        if (!applySuccess) {
            throw new RuntimeException(String.format("审核失败 %s", applyCheck.getId()));
        }

        // 更新审核状态 后面需要使用
        dbApplyCheck.setState(applyCheck.getState());
        dbApplyCheck.setUpdateDate(now);
        dbApplyCheck.setOperateUser(applyCheck.getOperateUser());

        // 审核结果入到异常管理
        iLogService.applyCheckLog(dbApplyCheck);

        // 审核后行为
        if (Objects.equals(applyCheck.getState(), ApplyCheckState.NO_PASSED.getCode())) {
            // 审核不通过
            if (Objects.equals(CandidateOperatorLogType.ID_FACE_SKIP_APPLY.getCode(), dbApplyCheck.getOperateType())) {
                // 人脸核身跳过不通过
                // 记录发送给学生的消息为未发送状态
                applyCheckMsgService.add(dbApplyCheck, WebsocketMsgSendState.NOT_SEND);

                // 更新人脸核身跳过不通过到kw
                KwExamCandidate examCandidate = new KwExamCandidate();
                examCandidate.setId(dbApplyCheck.getCandidateId());
                examCandidate.setFaceState(FaceState.MANUAL_NO_PASS.getCode());
                log.info("更新人脸核身跳过不通过到kw：{}", examCandidate);
                kwClient.updateCandidate(examCandidate);
            } else if (Objects.equals(CandidateOperatorLogType.DEVICE_CHECK_SKIP_APPLY.getCode(), dbApplyCheck.getOperateType())) {
                // 设备检测不通过
                // 记录消息为未发送状态
                applyCheckMsgService.add(dbApplyCheck, WebsocketMsgSendState.NOT_SEND);

                // 更新设备检测跳过不通过到kw
                KwExamCandidate examCandidate = new KwExamCandidate();
                examCandidate.setId(dbApplyCheck.getCandidateId());
                examCandidate.setDeviceCheckState(DeviceCheckState.STATE_THREE.getCode());
                examCandidate.setDeviceCheckDate(now);
                log.info("更新设备检测跳过不通过到kw：{}", examCandidate);
                kwClient.updateCandidate(examCandidate);
            }
        } else {
            // 审核通过
            if (Objects.equals(CandidateOperatorLogType.ID_FACE_SKIP_APPLY.getCode(), dbApplyCheck.getOperateType())) {
                // 人脸核身通过
                // 记录下审核通过记录到CandidatePass
                candidatePassService.setCandidatePassSteps(
                        dbApplyCheck.getExamId(), dbApplyCheck.getCandidateId(),
                        PassStepCode.PASS_STEP_FACE_VERIFY.getCode() + "",
                        applyCheck.getOperateUser(), applyCheck.getOperateUserName());


               // List<ExamConfigVO> examConfigList = iExamCacheService.getExamConfigCache(dbApplyCheck.getExamId());
               // Optional<ExamConfigVO> examConfig = examConfigList.stream().filter(e -> e.getConfigCode().equals(ExamConfigCode.EXAM_CONFIG_CODE_AUTHENTICATION.getCode())).findFirst();
               // if (examConfig.isPresent() && examConfig.get().getConfigValue().equals("true")) {
                    // 记录审核通过消息为未发送状态
                    applyCheckMsgService.add(dbApplyCheck, WebsocketMsgSendState.NOT_SEND);
                //}

                // 更新人脸核身跳过通过到kw
                KwExamCandidate examCandidate = new KwExamCandidate();
                examCandidate.setId(dbApplyCheck.getCandidateId());
                examCandidate.setFaceState(FaceState.MANUAL_PASS.getCode());
                log.info("更新人脸核身跳过通过到kw：{}", examCandidate);
                kwClient.updateCandidate(examCandidate);
            } else if (Objects.equals(CandidateOperatorLogType.DEVICE_CHECK_SKIP_APPLY.getCode(), dbApplyCheck.getOperateType())) {
                // 设备检测通过
                // 记录下设备检测通过到CandidatePass
                log.info("设备检测通过 记录下设备检测通过到CandidatePass：{} {} {} {} {}", dbApplyCheck.getExamId(), dbApplyCheck.getCandidateId(),
                        PassStepCode.PASS_STEP_DEVICE_DETECTION.getCode(), applyCheck.getOperateUser(), applyCheck.getOperateUserName());
                candidatePassService.setCandidatePassSteps(
                        dbApplyCheck.getExamId(), dbApplyCheck.getCandidateId(),
                        PassStepCode.PASS_STEP_DEVICE_DETECTION.getCode() + "",
                        applyCheck.getOperateUser(), applyCheck.getOperateUserName());

                // 记录审核通过消息为未发送状态
                applyCheckMsgService.add(dbApplyCheck, WebsocketMsgSendState.NOT_SEND);

                // 更新设备检测跳过通过到kw
                KwExamCandidate examCandidate = new KwExamCandidate();
                examCandidate.setId(dbApplyCheck.getCandidateId());
                examCandidate.setDeviceCheckState(DeviceCheckState.STATE_TWO.getCode());
                examCandidate.setDeviceCheckDate(now);
                log.info("更新设备检测跳过通过到kw：{}", examCandidate);
                kwClient.updateCandidate(examCandidate);
            }
        }

        // 审核后将es中待审核记录清除
        log.info("审核后将es中待审核记录清除：{}", dbApplyCheck.getId());
        esClient.deleteApplyCheck(Collections.singletonList(dbApplyCheck.getId()));

        // 发送消息给学生
        sendApplyCheckResult2Student(dbApplyCheck.getExamId(), dbApplyCheck.getCandidateId(),
                dbApplyCheck.getEventType(), dbApplyCheck.getOperateType());

        // 查询下一个待审核
        ApplyCheckDTO nextQuery = new ApplyCheckDTO();
        nextQuery.setExamId(dbApplyCheck.getExamId());
        nextQuery.setEventType(dbApplyCheck.getEventType());
        nextQuery.setOperateType(dbApplyCheck.getOperateType());
        return nextApplyCheck(nextQuery);
    }


    @Override
    public ApplyCheck nextApplyCheck(ApplyCheckDTO applyCheck) {
        return lambdaQuery()
                .eq(ApplyCheck::getExamId, applyCheck.getExamId())
                .eq(ApplyCheck::getEventType, applyCheck.getEventType())
                .eq(ApplyCheck::getOperateType, applyCheck.getOperateType())
                .eq(ApplyCheck::getState, ApplyCheckState.WAIT_HANDLE.getCode())
                .orderByDesc(ApplyCheck::getUpdateDate)
                .select(ApplyCheck::getId, ApplyCheck::getExamId, ApplyCheck::getCandidateId)
                .last("limit 1")
                .one();
    }

    @Override
    public DeviceVO applyCheckDeviceInfo(String applyCheckId) {
        ApplyCheck byId = getById(applyCheckId);
        if (byId == null) {
            log.error("审核记录{}不存在", applyCheckId);
            return null;
        }
        String operateContent = byId.getOperateContent();
        if (StringUtils.isBlank(operateContent) || !JSONUtil.isJson(operateContent)) {
            return DeviceVO.empty;
        }
        return JSON.parseObject(operateContent, DeviceVO.class);
    }

    @Override
    public void wsOpenCheckAndSendApplyResult(String examId, String candidateId) {
        List<ApplyCheckMsg> applyCheckMsgs = applyCheckMsgService.lambdaQuery()
                .eq(ApplyCheckMsg::getExamId, examId)
                .eq(ApplyCheckMsg::getCandidateId, candidateId)
                .list();
        sendApplyCheckResult2Student(applyCheckMsgs);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void sendApplyCheckResult2Student(List<ApplyCheckMsg> applyCheckMsgs) {
        log.info("给学生发送审核结果消息：{}", applyCheckMsgs);
        for (ApplyCheckMsg applyCheckMsg : applyCheckMsgs) {
            boolean needMsg = false;
            if (Objects.equals(applyCheckMsg.getState(), ApplyCheckState.NO_PASSED.getCode())) {
                if (Objects.equals(CandidateOperatorLogType.ID_FACE_SKIP_APPLY.getCode(), applyCheckMsg.getOperateType())) {
                    // 人脸审核不通过 发送websocket
                    WebsocketSendMessageDTO websocketSendMessageDTO = createWebsocketSendMessage(applyCheckMsg, MessageTypeCode.FACE_VERIFY_NOT_PASS.getCode(), MessageTypeCode.FACE_VERIFY_NOT_PASS.getMsg());
                    kwClient.sendMessage2Candidate(websocketSendMessageDTO);

                    // 记录已发送
                    needMsg = true;
                } else if (Objects.equals(CandidateOperatorLogType.DEVICE_CHECK_SKIP_APPLY.getCode(), applyCheckMsg.getOperateType())) {
                    // 设备检测不通过 发送websocket
                    WebsocketSendMessageDTO websocketSendMessageDTO = createWebsocketSendMessage(applyCheckMsg, MessageTypeCode.DEVICE_VERIFY_NOT_PASS.getCode(), MessageTypeCode.DEVICE_VERIFY_NOT_PASS.getMsg());
                    kwClient.sendMessage2Candidate(websocketSendMessageDTO);

                    // 记录已发送
                    needMsg = true;
                }
            } else {
                if (Objects.equals(CandidateOperatorLogType.DEVICE_CHECK_SKIP_APPLY.getCode(), applyCheckMsg.getOperateType())) {
                    // 设备检测通过 发送websocket
                    WebsocketSendMessageDTO websocketSendMessageDTO = createWebsocketSendMessage(applyCheckMsg, MessageTypeCode.DEVICE_VERIFY_PASS.getCode(), MessageTypeCode.DEVICE_VERIFY_PASS.getMsg());
                    kwClient.sendMessage2Candidate(websocketSendMessageDTO);

                    // 记录已发送
                    needMsg = true;
                }else if (Objects.equals(CandidateOperatorLogType.ID_FACE_SKIP_APPLY.getCode(), applyCheckMsg.getOperateType())) {
                    // 设备检测通过 发送websocket
                    WebsocketSendMessageDTO websocketSendMessageDTO = createWebsocketSendMessage(applyCheckMsg, MessageTypeCode.FACE_VERIFY_PASS.getCode(), MessageTypeCode.FACE_VERIFY_PASS.getMsg());
                    kwClient.sendMessage2Candidate(websocketSendMessageDTO);

                    // 记录已发送
                    needMsg = true;
                }
            }

            if (needMsg) {
                applyCheckMsgService.updateMsgSendState(applyCheckMsg.getExamId(), applyCheckMsg.getCandidateId(), applyCheckMsg.getEventType(), applyCheckMsg.getOperateType(), applyCheckMsg.getState(), WebsocketMsgSendState.SENDING);
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void sendApplyCheckResult2Student(String examId, String candidateId, int eventType, int operateType) {
        List<ApplyCheckMsg> applyCheckMsgs = applyCheckMsgService.lambdaQuery().eq(ApplyCheckMsg::getExamId, examId)
                .eq(ApplyCheckMsg::getCandidateId,candidateId)
                .eq(ApplyCheckMsg::getEventType, eventType)
                .eq(ApplyCheckMsg::getOperateType, operateType).list();
        sendApplyCheckResult2Student(applyCheckMsgs);
    }

    private WebsocketSendMessageDTO createWebsocketSendMessage(ApplyCheckMsg applyCheckMsg, String msgType, String message) {
        WebsocketSendMessageDTO websocketSendMessageDTO = new WebsocketSendMessageDTO();
        websocketSendMessageDTO.setId(applyCheckMsg.getId());
        websocketSendMessageDTO.setExamId(applyCheckMsg.getExamId());
        websocketSendMessageDTO.setCandidateId(applyCheckMsg.getCandidateId());
        MessageContentDTO messageContentDTO = new MessageContentDTO();
        messageContentDTO.setType(msgType);
        messageContentDTO.setMessage(message);
        messageContentDTO.setSendTime(System.currentTimeMillis() + "");
        websocketSendMessageDTO.setMessageContent(messageContentDTO);
        return websocketSendMessageDTO;
    }

    @Override
    public Integer deviceVerifyResult(String examId, String candidateId) {
        ApplyCheck applyCheck = lambdaQuery()
                .eq(ApplyCheck::getExamId, examId)
                .eq(ApplyCheck::getCandidateId, candidateId)
                .eq(ApplyCheck::getEventType, LogEventType.OPERATOR_LOG.getCode())
                .eq(ApplyCheck::getOperateType, CandidateOperatorLogType.DEVICE_CHECK_SKIP_APPLY.getCode())
                .orderByDesc(ApplyCheck::getCreateDate)
                .last("limit 1").one();
        if (Objects.isNull(applyCheck)) {
            // 没有待审核记录
            return -1;
        }
        return applyCheck.getState();
    }

    @Override
    public ApplyCheck getCandidateApplyCheck(String examId, String candidateId, int eventType, int operateType, Integer state) {
        return lambdaQuery().eq(ApplyCheck::getExamId, examId)
                .eq(ApplyCheck::getCandidateId,candidateId)
                .eq(ApplyCheck::getEventType, eventType)
                .eq(ApplyCheck::getOperateType, operateType)
                .eq(ApplyCheck::getState, state)
                .orderByDesc(ApplyCheck::getCreateDate)
                .last("limit 1").one();
    }

    @Override
    public void skipApply(ApplyCheckVO param, int opType, String type) {
        ApplyCheck dbApplyCheck = getCandidateApplyCheck(param.getExamId(), param.getCandidateId(),
                LogEventType.OPERATOR_LOG.getCode(), opType, ApplyCheckState.WAIT_HANDLE.getCode());
        LocalDateTime now = LocalDateTime.now();

        if (Objects.isNull(dbApplyCheck)) {
            // 数据库不存在待审核的设备检测信息则创建
            dbApplyCheck = new ApplyCheck();
            dbApplyCheck.setId(UUIDUtils.randomUUID());
            dbApplyCheck.setExamId(param.getExamId());
            dbApplyCheck.setCandidateId(param.getCandidateId());
            dbApplyCheck.setOperateType(opType);
            dbApplyCheck.setEventType(LogEventType.OPERATOR_LOG.getCode());
            dbApplyCheck.setCreateDate(now);
            dbApplyCheck.setUpdateDate(now);
            if (ApplyCheckSkipTypeCode.DEVICE_CHECK.getCode().equals(type)  && param.getDeviceCheckResult() != null) {
                dbApplyCheck.setOperateContent(JSON.toJSONString(param.getDeviceCheckResult()));
            }
            else if (ApplyCheckSkipTypeCode.MANUAL_REVIEW_FACE_APPLY.getCode().equals(type)  && param.getManualReviewFaceApply() != null) {
                dbApplyCheck.setOperateContent(JSON.toJSONString(param.getManualReviewFaceApply()));
            }
            log.info("新增设备检测跳过申请：{}", dbApplyCheck);

            save(dbApplyCheck);
        } else {
            // 存在就更新旧的未审核通过的设备
            if (ApplyCheckSkipTypeCode.DEVICE_CHECK.getCode().equals(type) && param.getDeviceCheckResult() != null) {
                dbApplyCheck.setOperateContent(JSON.toJSONString(param.getDeviceCheckResult()));
            }
            else if (ApplyCheckSkipTypeCode.MANUAL_REVIEW_FACE_APPLY.getCode().equals(type) && param.getManualReviewFaceApply() != null) {
                dbApplyCheck.setOperateContent(JSON.toJSONString(param.getManualReviewFaceApply()));
            }
            dbApplyCheck.setUpdateDate(now);
            log.info("更新设备检测跳过申请：{}", dbApplyCheck);
            updateById(dbApplyCheck);
        }
        // 人脸核身跳过申请不计入轨迹
        if(ApplyCheckSkipTypeCode.DEVICE_CHECK.getCode().equals(type)) {
            iLogService.applySkipLog(dbApplyCheck, ApplyCheckSkipTypeCode.DEVICE_CHECK, now);
        } else if(ApplyCheckSkipTypeCode.MANUAL_REVIEW_FACE_APPLY.getCode().equals(type)) {
            iLogService.applySkipLog(dbApplyCheck, ApplyCheckSkipTypeCode.MANUAL_REVIEW_FACE_APPLY, now);

        }
    }

    @Override
    public List<String> findApplyCheckCandidateIdAuditPassList(String examId, String candidateIds) {
        List<ApplyCheckCandidateVO> applyCheckCandidateIdList = baseMapper.findApplyCheckCandidateIdList(examId, candidateIds.contains(",") ? Arrays.asList(candidateIds.split(",")) : Collections.singletonList(candidateIds));
        if (!applyCheckCandidateIdList.isEmpty()) {
            Map<String, ApplyCheckCandidateVO> applyCheckCandidateVOMap = applyCheckCandidateIdList.stream().collect(
                    Collectors.toMap(ApplyCheckCandidateVO::getCandidateId, Function.identity(), (c1, c2) -> {
                        if (Objects.nonNull(c1.getUpdateDate()) && Objects.nonNull(c2.getUpdateDate())) {
                            return c1.getUpdateDate().isAfter(c2.getUpdateDate()) ? c1 : c2;
                        } else if (Objects.isNull(c1.getUpdateDate()) && Objects.isNull(c2.getUpdateDate())) {
                            return c1.getCreateDate().isAfter(c2.getCreateDate()) ? c1 : c2;
                        } else {
                            return Objects.isNull(c2.getUpdateDate()) ? c1 : c2;
                        }
                    }));
            return applyCheckCandidateVOMap.values().stream().filter(v -> 1 == v.getState()).map(ApplyCheckCandidateVO::getCandidateId).collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    @Override
    public List<CandidateLogBeginTimeVO> getApplyCheckUnPassFaceTime(String examId) {
        return baseMapper.getApplyCheckUnPassFaceTime(examId);
    }
}
